/*
	NounShipLogic.cpp

	This file contains all the logic code embedded into a ship for use by the AI
	(c)2000 Palestar Inc, Richard Lyle
*/

#include "Debug/Assert.h"
#include "Debug/Trace.h"
#include "Math/Helpers.h"
#include "VerbEnterOrbit.h"
#include "VerbOrderShip.h"
#include "VerbCommandShip.h"
#include "VerbUnload.h"
#include "VerbLoad.h"
#include "VerbDistress.h"
#include "VerbBuildStructure.h"

#include "NounShip.h"
#include "NounUnit.h"
#include "NounBeacon.h"
#include "NounStructure.h"
#include "NounJumpGate.h"
#include "NounJumpTarget.h"
#include "NounNebula.h"
#include "NounStar.h"
#include "NounProjectile.h"
#include "NounDropTarget.h"
#include "GadgetBeamWeapon.h"
#include "GadgetReload.h"
#include "GadgetBeacon.h"
#include "GadgetCloak.h"
#include "GadgetBuild.h"
#include "GadgetECCM.h"
#include "GadgetECM.h"
#include "GadgetELF.h"
#include "StructurePort.h"
#include "StructureHub.h"
#include "StructureBarracks.h"
#include "StructurePower.h"
#include "StructurePort.h"
#include "StructureDome.h"
#include "StructureFarm.h"
#include "StructureFactory.h"
#include "StructureMine.h"
#include "StructureLab.h"
#include "StructureDefense.h"
#include "StructureDepot.h"
#include "StructureShipyard.h"
#include "StructureShield.h"
#include "Resource.h"
#include "GameContext.h"

//----------------------------------------------------------------------------

static Constant		SHIP_AI_RATE( "SHIP_AI_RATE", TICKS_PER_SECOND * 5 );					// updateOrder() is called every SHIP_AI_RATE ticks
static Constant		MAX_SHIP_ORBIT_DISTANCE( "MAX_SHIP_ORBIT_DISTANCE", 150.0f );
static Constant		SHIP_ORBIT_VELOCITY( "SHIP_ORBIT_VELOCITY", 0.0f );						// orbit velocity of ship when it enters orbit
static Constant		SHIP_FOLLOW_DISTANCE( "SHIP_FOLLOW_DISTANCE", 50.0f );					// what range to we try to move within when following					
static Constant		SHIP_SHADOW_DISTANCE( "SHIP_SHADOW_DISTANCE", 1000.0f );	
static Constant		SHIP_EVADE_DISTANCE( "SHIP_EVADE_DISTANCE", 200.0f);					// what range do we begin evading our target
static Constant		SHIP_ENGAGE_DISTANCE( "SHIP_ENGAGE_DISTANCE", 75.0f );					// how close to move when engaging a target
static Constant		SHIP_FIND_RANGE( "SHIP_FIND_RANGE", 250000.0f );
static Constant		SHIP_DEFAULT_SPEED( "SHIP_DEFAULT_SPEED", 1.0f );
static Constant		SHIP_ENGAGE_SPEED( "SHIP_ENGAGE_SPEED", 0.6f );

static Constant		FIND_ATTACK_TARGET_DISTANCE( "FIND_ATTACK_TARGET_DISTANCE", 5000000.0f );
static Constant		FIND_ENEMY_DISTANCE_VALUE("FIND_ENEMY_DISTANCE_VALUE", 0.05f );			// findEnemyShip() uses this to determine the value of distance versus other factors
static Constant		MAX_ORDER_TIME( "ORDER_TIME", TICKS_PER_SECOND * 600 );
static Constant		MAX_ORDER_RANGE( "MAX_ORDER_RANGE", 2500000.0f );						// maximum range we will return a positive score for a ship

static Constant		USE_JUMP_DISTANCE( "USE_JUMP_DISTANCE", 2000.0f );					// when moving to a target, at what range do we use the jump drives
static Constant		DEFAULT_SHIP_BRAVERY( "DEFAULT_SHIP_BRAVERY", 1.0f );
static Constant		FIND_ENEMY_SHIP_RANGE( "FIND_ENEMY_SHIP_RANGE", 5000.0f );

//! How often will an AI ship send a distress when being attacked...
static Constant		SHIP_AI_ATTACK_DISTRESS( "SHIP_AI_ATTACK_DISTRESS", 300.0f );
//! How often will an AI ship request resupply...
static Constant		SHIP_AI_SUPPLY_DISRESS( "SHIP_AI_SUPPLY_DISRESS", 300.0f );
// How does distance affect the value of a target ... the distance of the target is multiplied
// by this value then subtracted from the target value... the further away an object is, the lower
// it's value.
static Constant		DISTANCE_VALUE_SCALE( "DISTANCE_VALUE_SCALE", 0.5f );
//! Values scales
static Constant		BLOCKADE_VALUE_SCALE( "BLOCKADE_VALUE_SCALE", 1.5f );
static Constant		SHIPYARD_VALUE_SCALE( "SHIPYARD_VALUE_SCALE", 2.0f );

//! At what percent does a ship need to refuel jump drives
static Constant		NEED_FUEL_PERCENT( "NEED_FUEL_PERCENT", 0.30f );		// 30%
//! When will needAmmo() return true...
static Constant		NEED_AMMO_PERCENT( "NEED_AMMO_PERCENT", 0.30f );		// 30%
//! What is the minimum distance of a planet to be considered for reconnaissance..
static Constant		MIN_RECON_DISTANCE( "MIN_RECON_DISTANCE", 10000.0f );
//! How close does a ship need to be to complete the recon order
static Constant		RECON_DISTANCE( "RECON_DISTANCE", 500.0f );
//! How close does a ship need to be to complete the move order
static Constant		MOVE_DISTANCE( "MOVE_DISTANCE", 200.0f );
//! used by findUnits() to calculate the value of a planet...
static Constant		UNIT_VALUE( "UNIT_VALUE", 1000.0f );
//! How many units should be left to defend a planet..
static Constant		MIN_PLANET_UNITS( "MIN_PLANET_UNITS", 20.0f );
//! How often to pick a random evade vector in seconds.
static Constant		EVADE_VECTOR_INTERVAL( "EVADE_VECTOR_INTERVAL", 10.0f );
//! What is the chance we will jump to intercept our engage target
static Constant		JUMP_INTERCEPT_CHANCE( "JUMP_INTERCEPT_CHANCE", 20.0f );
//! How many seconds ahead of our target should we jump in seconds..
static Constant		JUMP_INTERCEPT_TIME( "JUMP_INTERCEPT_TIME", 10.0f );
//! How far away should our target be before we consider jumping to intercept
static Constant		MIN_INTERCEPT_DISTANCE( "MIN_INTERCEPT_DISTANCE", 600.0f );
//! How low does the hull need to get before we consider an emergency jump
static Constant		EMERGENCY_JUMP_DAMAGE( "EMERGENCY_JUMP_DAMAGE", 0.05f );
//! Chance that we will actually try to jump out..
static Constant		EMERGENCY_JUMP_CHANCE( "EMERGENCY_JUMP_CHANCE", 25.0f );

//---------------------------------------------------------------------------------------------------

template<typename T>
T * FindChild( BaseNode * a_pNode )
{
	for(int i=0;i<a_pNode->childCount();++i)
	{
		BaseNode * pChild = a_pNode->child(i);
		if ( pChild->factory()->classKey() == T::classKey() )
			return (T *)a_pNode->child(i);
	}
	return NULL;
}

template<typename T>
int CountChildren( BaseNode * a_pNode )
{
	int nCount = 0;
	for(int i=0;i<a_pNode->childCount();++i)
	{
		if ( WidgetCast<T>( a_pNode->child(i) ) != NULL )
			++nCount;
	}
	return nCount;
}

static float RealDistanceFromTarget( NounShip * pSelf, Noun * pTarget )
{
	float fDistance = (pSelf->worldPosition() - pTarget->worldPosition()).magnitude();
	fDistance -= pSelf->radius();
	fDistance -= pTarget->radius();

	return fDistance;
}

//---------------------------------------------------------------------------------------------------

bool NounShip::isOrderValid( Order nOrder, Noun * pTarget, NounShip * pFrom )
{
	switch( nOrder )
	{
	case NOORDER:			// no order
	case HOLD:				// stay at current position
		break;
	case ATTACK:			// attack target
		if (! pFrom->validateTarget( pTarget ) )
			return false;
		if ( WidgetCast<NounShip>( pTarget ) == NULL && 
			WidgetCast<NounPlanet>( pTarget ) == NULL )
			return false;
		if ( pFrom->isFriend( pTarget ) )
			return false;
		break;
	case DEFEND:			// defend target
		if (! pFrom->validateTarget( pTarget ) )
			return false;
		if ( WidgetCast<NounShip>( pTarget ) == NULL && 
			WidgetCast<NounPlanet>( pTarget ) == NULL )
			return false;
		break;
	case CAPTURE:			// capture target
		if (! pFrom->validateTarget( pTarget ) )
			return false;
		if ( WidgetCast<NounPlanet>( pTarget ) == NULL && 
			WidgetCast<NounShip>( pTarget ) == NULL )
			return false;	// only ships/planets can be captured
		if ( pFrom->isFriend( pTarget ) )
			return false;
		break;
	case MOVE:				// move to target
		if (! pFrom->validateTarget( pTarget ) )
			return false;
		break;
	case RECON:
		if (! pFrom->validateTarget( pTarget ) )
			return false;
		if ( WidgetCast<NounPlanet>( pTarget ) == NULL )
			return false;	// target is not a planet
		break;
	case RELOAD:			// reload/repair target
		if (! pFrom->validateTarget( pTarget ) )
			return false;
		if ( WidgetCast<NounShip>( pTarget ) == NULL )
			return false;		// target is not a ship
		if ( !pFrom->isFriend( pTarget ) )
			return false;
		break;
	case BEACON:			// attach beacon to target
		if (! pFrom->validateTarget( pTarget ) )
			return false;
		if ( WidgetCast<NounStructure>( pTarget ) == NULL && 
			WidgetCast<NounShip>( pTarget ) == NULL )
			return false;	// only ships/planets can be captured
		if ( NounBeacon::findBeacon( pTarget, pFrom ) != NULL )
			return false;
		if ( pFrom->isFriend( pTarget ) )
			return false;
		break;
	case TRADE:				// transfer resources
		if (! pFrom->validateTarget( pTarget ) )
			return false;
		if ( WidgetCast<NounPlanet>( pTarget ) == NULL )
			return false;	// target is not a planet
		if ( (((NounPlanet *)pTarget)->flags() & NounPlanet::FLAG_HAS_PORT) == 0 )
			return false;	// target has no port
		if ( !pFrom->isFriend( pTarget ) )
			return false;
		break;
	case BUILD:
		if (! pFrom->validateTarget( pTarget ) )
			return false;
		if ( WidgetCast<NounPlanet>( pTarget ) == NULL )
			return false;	// target is not a planet
		if ( !pFrom->isFriend( pTarget ) )
			return false;	// planet is enemy/neutral
		break;
	case FALLBACK:
		if (! pFrom->validateTarget( pTarget ) )
			return false;
		if ( !pFrom->isFriend( pTarget ) )
			return false;
		break;		
	}

	return true;
}

//----------------------------------------------------------------------------

// returns a score which can be used to determine how well this ship can accomplish that order, any scores zero or below should be not be assigned
float NounShip::shouldOrder( Order nOrder, Noun * pTarget, NounShip * pFrom )
{
	if ( pFrom == NULL )
		return 0;
	if (! canOrder( nOrder, pTarget,  pFrom ) )
		return 0;

	switch( nOrder )
	{
	case NOORDER:			// no order
	case HOLD:				// stay at current position
		return 0;
	case ATTACK:			// attack target
		{
			// make sure this ship is a combat ship
			if ( type() < SCOUT &&  type() > DREAD )
				return 0;		// this ship is not a combat ship, do not give it attack orders!
			if (! canAttack( pTarget ) )
				return 0;		// this ship doesn't have the weapons to attack this target

			float fDistance = (worldPosition() - pTarget->worldPosition()).magnitude();
			// first, calculate score based on distance to this target
			float fDistanceScore = 1.0f - Min(fDistance / MAX_ORDER_RANGE, 1.0f);
			// adjust score based on target
			float fTargetScore = scoreTarget( pTarget );

			return fDistanceScore * fTargetScore;
		}
		break;
	case DEFEND:			// defend target
		{
			// make sure this ship is a combat ship
			if ( type() < SCOUT && type() > DREAD )
				return 0;		// this ship is not a combat ship, do not give it defend orders!

			float fDistance = (worldPosition() - pTarget->worldPosition()).magnitude();
			// first, calculate score based on distance to this target
			float fDistanceScore = 1.0f - Min(fDistance / MAX_ORDER_RANGE, 1.0f);
			// adjust score based on target
			float fTargetScore = scoreTarget( pTarget );

			return fDistanceScore * fTargetScore;
		}
		break;
	case CAPTURE:			// capture target
	case TRADE:				// transfer resources
		{
			float fDistance = (worldPosition() - pTarget->worldPosition()).magnitude();
			// first, calculate score based on distance to this target
			float fDistanceScore = 1.0f - Min(fDistance / MAX_ORDER_RANGE, 1.0f);
			float fTargetScore = scoreTarget( pTarget );
			float fCargoScore = Clamp( maxCargo() / 20.0f, 0.0f, 1.0f);

			return fDistanceScore * fTargetScore * fCargoScore;
		}
		break;
	case MOVE:				// move to target
	case RELOAD:			// reload/repair target
	case BUILD:
		{
			float fDistance = (worldPosition() - pTarget->worldPosition()).magnitude();
			// first, calculate score based on distance to this target
			float fDistanceScore = 1.0f - Min(fDistance / MAX_ORDER_RANGE, 1.0f);

			return fDistanceScore;
		}
	case BEACON:			// attach beacon to target
	case RECON:
		{
			if ( type() != SCOUT )
				return 0;

			float fDistance = (worldPosition() - pTarget->worldPosition()).magnitude();
			// first, calculate score based on distance to this target
			float fDistanceScore = 1.0f - Min(fDistance / MAX_ORDER_RANGE, 1.0f);
			float fTargetScore = scoreTarget( pTarget );

			return fDistanceScore * fTargetScore;
		}
		break;
	}

	return 0;
}

bool NounShip::canOrder( Order nOrder, Noun * pTarget, NounShip * pFrom )
{
	if ( pFrom == NULL )
		return false;	// invalid pointers
	if ( pFrom->rank() < rank() )
		return false;	// not enough rank
	if (! isFriend( pFrom ) )
		return false;	// not the same fleet

	// check if we have an existing order from a higher/equal ranking player, do not allow the order then...
	// allow the order if it's from the same player, a higher ranking player, or from this ship
	if ( m_Order != NounShip::NOORDER && m_pOrderFrom.valid() 
		&& m_pOrderFrom != pFrom && pFrom != this 
		&& m_pOrderFrom->rank() >= pFrom->rank() )
	{
		// current order is from a higher ranking player, cannot change this order
		return false;
	}

	if (! isOrderValid( nOrder, pTarget, pFrom ) )
		return false;

	// check that the order is valid for this ship
	switch( nOrder )
	{
	case RELOAD:			// reload/repair target
		if ( GadgetReload::getGadgetReload( this ) == NULL )
			return false;
		break;
	case BEACON:			// attach beacon to target
		if ( GadgetBeacon::getGadgetBeacon( this ) == NULL )
			return false;
		break;
	case BUILD:
		if ( GadgetBuild::getGadgetBuild( this ) == NULL )
			return false;	// ship has no build device
		break;
	}

	return true;
}

void NounShip::verbOrder( Order order, Noun * pTarget )
{
	Verb::Ref( new VerbOrderShip( this, order, pTarget, this ) );
}

void NounShip::verbOrder( Order order, Noun * pTarget, NounShip * pFrom )
{
	Verb::Ref( new VerbOrderShip( this, order, pTarget, pFrom ) );
}

void NounShip::setOrder( Order order, Noun * pTarget, NounShip * pFrom )
{
	m_Order = order;
	m_nOrderTime = 0;
	m_OrderTarget = pTarget;
	m_pOrderFrom = pFrom;
}

bool NounShip::canSendOrder( Order nOrder, Noun * pTarget )
{
	return isOrderValid( nOrder, pTarget, this );
}

NounShip * NounShip::sendOrder( Order nOrder, Noun * pTarget )
{
	NounShip * pOrderShip = NULL;

	// find the best ship to send this order too
	Queue< NounShip * > fleet;

	// find all ships in the universe in our fleet
	GameContext * pContext = (GameContext *)context();
	ASSERT( pContext );
	for(int i=0;i<pContext->zoneCount();i++)
	{
		NodeZone * pZone = pContext->zone(i);
		for(int j=0;j<pZone->childCount();j++)
		{
			NounShip * pShip = WidgetCast<NounShip>( pZone->child(j) );
			if ( pShip != NULL && isFriend( pShip ) )
				fleet.push( pShip );
		}
	}

	float fBestScore = 0.0f;
	while( fleet.valid() )
	{
		NounShip * pShip = *fleet;
		fleet.next();
		
		if ( pShip == this || pShip == pTarget )
			continue;		// skip self or objective of order
		if ( pShip->order() != NounShip::NOORDER && pShip->orderFrom() == this )
			continue;		// skip ships that already have an order from this ship

		float fScore = pShip->shouldOrder( nOrder, pTarget, this );
		if ( fScore > fBestScore )
		{
			pOrderShip = pShip;
			fBestScore = fScore;
		}
	}

	if ( pOrderShip != NULL )
	{
		message( CharString().format( "<color;ffffff>Tactical: Order Sent, %s %s %s.", 
			pOrderShip->name(), orderText( nOrder ), pTarget ? pTarget->name() : "") );

		Verb::Ref( new VerbOrderShip( pOrderShip, nOrder, pTarget, this ) );
	}
	else
		message( "<color;ffffff>Tactical: No ships available for order." );

	return pOrderShip;
}

NounShip::Order NounShip::bestOrder( Noun * pTarget )
{
	if ( pTarget == NULL )
		return NounShip::NOORDER;

	if ( WidgetCast<NounDropTarget>( pTarget ) )
		pTarget = ((NounDropTarget *)pTarget)->planet();
	
	Noun * pRoot = pTarget->zoneParent();
	if ( WidgetCast<NounPlanet>( pRoot ) )
	{
		if ( isFriend( pTarget ) )
		{
			if ( GadgetBuild::getGadgetBuild( this ) != NULL )
				return NounShip::BUILD;

			return NounShip::DEFEND;
		}
		else
			return NounShip::CAPTURE;
	}
	else if ( WidgetCast<NounShip>( pRoot ) )
	{
		if ( isFriend( pTarget ) )
		{
			if ( GadgetReload::getGadgetReload( this ) != NULL )
				return NounShip::RELOAD;
			return NounShip::DEFEND;
		}
		else
			return NounShip::ATTACK;
	}
	else if ( WidgetCast<NounTarget>( pTarget ) )
		return NounShip::MOVE;

	return NounShip::NOORDER;
}

//----------------------------------------------------------------------------

bool NounShip::canCommand( Command nCommand, Noun * pTarget )
{
	switch( nCommand )
	{
	case ORBIT:
	case REFUEL:
		if (! validateTarget( pTarget ) )
			return false;		// needs a target
		if ( pTarget == this )
			return false;		// cannot orbit ourselves
		if (! VerbEnterOrbit::canOrbit( this, pTarget ) )
			return false;		// cannot orbit this object
		return true;
	case SHADOW:
		if (! validateTarget( pTarget ) )
			return false;
		if ( WidgetCast<NounShip>( pTarget ) == NULL )
			return false;
		if ( pTarget == this )
			return false;
		return true;
	case FOLLOW:
	case EVADE:
		if (! validateTarget( pTarget ) )
			return false;
		if ( pTarget == this )
			return false;
		if(pTarget->parent() == this) // if we are carrying this object
			return false;
		return true;
	case ENGAGE:
		if (! validateTarget( pTarget ) )
			return false;
		if ( isFriend( pTarget ) )
			return false;
		if ( pTarget == this )
			return false;
		return true;
	case STOP:
	case NOCOMMAND:
		return true;
	}

	return false;
}

void NounShip::verbCommand( Command command, Noun * pTarget )
{
	if ( canCommand( command, pTarget ) && !isCommand( command, pTarget ) )
		Verb::Ref( new VerbCommandShip( this, command, pTarget ) );
}

bool NounShip::isCommand( Command nCommand, Noun * pTarget )
{
	return m_Command == nCommand && pTarget == m_CommandTarget;
}

bool NounShip::setCommand( Command command, Noun * pTarget )
{
	m_Command = command;
	m_CommandTarget = pTarget;
	return true;
}

NounPlanet * NounShip::isTargetPlanet( Noun * pTarget )
{
	if ( WidgetCast<NounStructure>( pTarget ) )
		pTarget = ((NounStructure *)pTarget)->planet();
	else if ( WidgetCast<NounDropTarget>( pTarget ) )
		pTarget = ((NounDropTarget *)pTarget)->planet();

	return WidgetCast<NounPlanet>( pTarget );
}

//---------------------------------------------------------------------------------------------------

bool NounShip::updateLogic( dword nTick )
{
	if ( userId() != 0 )
		return false;
	if ( ! isLocal() || nTick < m_nLogicTick )
		return false;

	//PROFILE_START( "NounShip::updateLogic()");

	m_nLogicTick = nTick + SHIP_AI_RATE;
	m_nOrderTime += SHIP_AI_RATE;

	// if we have been attacked, consider other options than following our current order..
	if ( m_bAttacked )
	{
		m_bAttacked = false;		// clear the flag

		// check the ship that is attacking us, consider running or attacking it..
		NounShip * pAttacker = WidgetCast<NounShip>( m_pLastAttacker );
		if ( pAttacker != NULL && isEnemy( pAttacker ) )
		{
			// get our current hull damage, 1.0 = none, 0.0 = dead
			float fDamage = damageRatioInv();
			// TODO: We should also consider shielding and armor damage too...
			// > 1.0 then the ship is more powerful than me, less < 1.0 the ship is weaker
			float fComparison = float( pAttacker->value() ) / (value() * fDamage);	
			if ( fComparison > 1.0f )
			{
				// consider falling back... the better the other ship, the more likely we'll retreat...
				int nFallbackChance = static_cast<int>( (fDamage * 100) - (5 * fComparison) );
				if ( (rand() % 100) > nFallbackChance )
				{
					Noun * pTarget = findDepot();
					if ( !pTarget )
						pTarget = findPlanet( true );

					if ( pTarget )
						verbOrder( FALLBACK, pTarget );
				}
			}
		}
	}

	// check the order, set the ship to NOORDER if needed
	if (! canOrder( m_Order, m_OrderTarget, this ) || 
		(m_Order != NOORDER && m_nOrderTime > MAX_ORDER_TIME) )
	{
		// end the order
		verbOrder( NOORDER, NULL );
	}

	bool bOrderValid = false;

	// update high-level AI
	switch( m_Order )
	{
	case NOORDER:
		if (! findBestOrder() )
			returnHome();
		bOrderValid = true;
		break;
	case ATTACK:
		bOrderValid = updateAttackOrder();
		break;
	case DEFEND:
		bOrderValid = updateDefendOrder();
		break;
	case CAPTURE:
		bOrderValid = updateCaptureOrder();
		break;
	case RECON:
		bOrderValid = updateReconOrder();
		break;
	case MOVE:
		bOrderValid = updateMoveOrder();
		break;
	case RELOAD:
		bOrderValid = updateReloadOrder();
		break;
	case BEACON:
		bOrderValid = updateBeaconOrder();
		break;
	case HOLD:
		bOrderValid = updateHoldOrder();
		break;
	case TRADE:
		bOrderValid = updateTradeOrder();
		break;
	case BUILD:
		bOrderValid = updateBuildOrder();
		break;
	case FALLBACK:
		bOrderValid = updateFallbackOrder();
		break;
	}

	// cancel the order if it's no longer valid..
	if ( !bOrderValid && m_Order != NOORDER )
		verbOrder( NOORDER, NULL );

	// update the AI on all gadgets...
	for(int i=0;i<m_Gadgets.size();i++)
		m_Gadgets[i]->updateLogic();

	//PROFILE_END();

	return true;
}

bool NounShip::updateAttackOrder()
{
	Noun * pTarget = m_OrderTarget;
	if (! pTarget )
		return false;
	if ( m_Command != EVADE && m_Command != ENGAGE )
		verbCommand( ENGAGE, pTarget );
	return true;
}

bool NounShip::updateDefendOrder()
{
	Noun * pTarget = m_OrderTarget;
	if (! pTarget )
		return false;

	NounShip * pEnemy = findEnemyShip( DEFAULT_SHIP_BRAVERY, FIND_ENEMY_SHIP_RANGE );
	if ( pEnemy != NULL )
	{
		if ( m_Command != EVADE && m_Command != ENGAGE )
			verbCommand( ENGAGE, pEnemy );
	}
	else
	{
		if ( canOrbit( pTarget ) )
			verbCommand( ORBIT, pTarget );
		else if ( isFriend( pTarget ) )
			verbCommand( FOLLOW, pTarget );
	}

	return true;
}

bool NounShip::updateCaptureOrder()
{
	Noun * pTarget = m_OrderTarget;
	if (! pTarget )
		return false;
	if ( isFriend( pTarget ) )
		return false;

	// m_bLoadUnits is set to true when we have no units to drop on our target planet..
	if ( m_bLoadUnits )
	{
		if ( m_Command == ORBIT )
			return true;		// in route to planet to orbit...

		// first, check if we're in orbit around a friendly planet.. 
		NounPlanet * pPlanet = WidgetCast<NounPlanet>( orbiting() );
		if ( validateTarget( pPlanet ) && isFriend( pPlanet ) )
		{
			int nUnits = pPlanet->friendlyUnitCount();
			int nMinUnits = (int)MIN_PLANET_UNITS;

			// find units on the planet we are orbiting and load them...
			for(int i=0;i<pPlanet->childCount() && nUnits > nMinUnits;++i)
			{
				NounUnit * pUnit = WidgetCast<NounUnit>( pPlanet->child(i) );
				if ( canLoad( pUnit ) )
				{
					verbLoad( pUnit );
					nUnits -= 1;
				}
			}
			
			if ( podCount() > 0 )
				return true;

			// no pod in transit, either we are full of units or the planet has none.. 
			if ( unitCount() > 0 )
			{
				// we have some units, so proceed to drop phase..
				m_bLoadUnits = false;
				return true;
			}
			// we have no units on board, fall through and search for a planet with units..
		}

		// find a planet with ground units and proceed to load..
		pPlanet = findUnits();
		if ( validateTarget( pPlanet ) )
		{
			verbCommand( ORBIT, pPlanet );
			return true;
		}

		// no friendly planet with units is available..
		if ( unitCount() > 0 )
		{
			// we didn't find a planet, but we have some units so proceed to drop them on some planet..
			m_bLoadUnits = false;
			return true;
		}
		return false;
	}

	int nDropPending = 0;
	for(int i=0;i<childCount();i++)
	{
		NounUnit * pUnit = WidgetCast<NounUnit>( child(i) );
		if (! pUnit )
			continue;

		if ( canUnload( pTarget, pUnit ) )
			verbUnload( pTarget, pUnit );
		else
			nDropPending += 1;
	}

	if ( nDropPending > 0 )
	{
		// we have units so proceed to our target...
		if ( canOrbit( pTarget ) )
			verbCommand( ORBIT, pTarget );
		else
			verbCommand( FOLLOW, pTarget );
	}
	else
	{
		// no units to drop, proceed to load phase..
		m_bLoadUnits = true;
	}
	
	return true;
}

bool NounShip::updateReconOrder()
{
	Noun * pTarget = m_OrderTarget;
	if (! pTarget )
		return false;
	if ( WidgetCast<NounStructure>( pTarget ) || WidgetCast<NounUnit>( pTarget ) )
		pTarget = WidgetCast<Noun>( pTarget->parent() );

	float fDistance = RealDistanceFromTarget( this, pTarget ); //(worldPosition() - pTarget->worldPosition()).magnitude();
	if ( fDistance < RECON_DISTANCE )
		return false;		// close enough, order is complete..

	if ( canOrbit( pTarget ) )
		verbCommand( ORBIT, pTarget );
	else
		verbCommand( FOLLOW, pTarget );

	return true;
}

bool NounShip::updateMoveOrder()
{
	Noun * pTarget = m_OrderTarget;
	if (! pTarget )
		return false;
	if ( WidgetCast<NounStructure>( pTarget ) || WidgetCast<NounUnit>( pTarget ) )
		pTarget = WidgetCast<Noun>( pTarget->parent() );

	if ( canOrbit( pTarget ) )
	{
		if ( orbiting() == pTarget )
			return false;

		verbCommand( ORBIT, pTarget );
	}
	else
	{
		float fDistance = RealDistanceFromTarget( this, pTarget ); //(worldPosition() - pTarget->worldPosition()).magnitude();
		if ( fDistance < MOVE_DISTANCE )
			return false;

		verbCommand( FOLLOW, pTarget );
	}

	return true;
}


bool NounShip::updateReloadOrder()
{
	if ( GadgetReload::getGadgetReload( this ) == NULL )
		return false;
	Noun * pTarget = m_OrderTarget;
	if (! pTarget )
		return false;
	NounShip * pTargetShip = WidgetCast<NounShip>( pTarget );
	if (! pTargetShip )
		return false;
	if (! pTargetShip->needReload( this ) && !pTargetShip->needRepair( this ) )
		return false;

	// gadget will use on the ship automatically once we are in range..
	verbCommand( FOLLOW, pTargetShip );

	return true;
}

bool NounShip::updateBeaconOrder()
{
	Noun * pTarget = m_OrderTarget;
	if (! pTarget )
		return false;

	if ( canOrbit( pTarget ) )
		verbCommand( ORBIT, pTarget );
	else
		verbCommand( FOLLOW, pTarget );

	bool bCanBeacon = false;
	for(int i=0;i<m_Gadgets.size();i++)
	{
		NounGadget * pGadget = m_Gadgets[i];
		if ( WidgetCast<GadgetBeacon>( pGadget ) )
		{
			if ( ((GadgetBeacon *)pGadget)->ammo() > 0 )
			{
				bCanBeacon = true;
				if ( !pGadget->useActive()  && pGadget->usable( pTarget, false ) )
					useGadget( pGadget, pTarget, false );			// attempt to beacon the target
			}
		}
	}

	return bCanBeacon;
}

bool NounShip::updateHoldOrder()
{
	verbCommand( STOP, NULL );
	return true;
}

bool NounShip::updateTradeOrder()
{
	Noun * pTarget = m_OrderTarget;
	if (! pTarget )
		return false;
	NounPlanet * pHome = WidgetCast<NounPlanet>( pTarget );
	if (! validateTarget( pHome ) )
		return false;

	if ( m_Command == NOCOMMAND )
	{
		NounPlanet * pOrbiting = WidgetCast<NounPlanet>( orbiting() );
		if ( validateTarget( pOrbiting ) )
		{
			// if we have not yet, sell all cargo
			if (! m_bCargoSold )
			{
				m_bCargoSold = true;
				sellCargo();
			}
			else if ( isMaxFuel() )
			{
				// wait until our fuel is max before buying and moving onto the next destination
				// find our next trade destination
				NounPlanet * pNext = findTradeTarget( pHome );
				if (! validateTarget( pNext ) )
					return false;		// no valid trade target found, cancel this order

				if ( pNext != pOrbiting )
				{
					// buy cargo for that destination
					if (! buyCargo( pNext ) )
					{
						m_bCargoSold = false;
						// now go orbit our destination
						verbCommand( ORBIT, pNext );
					}
				}
			}
		}
		else
		{
			// go back and orbit our trade planet...
			verbCommand( ORBIT, pHome );
		}
	}

	return true;
}

bool NounShip::updateBuildOrder()
{
	Noun * pTarget = m_OrderTarget;
	if (! pTarget )
		return false;
	GadgetBuild * pBuildGadget = GadgetBuild::getGadgetBuild( this );
	if (! pBuildGadget )
		return false;
	if ( pBuildGadget->useActive() )
		return true;	// currently building something..
	NounPlanet * pPlanet = WidgetCast<NounPlanet>( pTarget );
	if (! validateTarget( pPlanet ) )
		return false;
	if ( pPlanet->structureCount() >= pPlanet->maxStructures() )
		return false;

	// first, get into orbit of the planet we need to build on...
	if ( orbiting() != pPlanet )
	{
		verbCommand( ORBIT, pPlanet );
		return true;
	}

	// build the next needed structure...
	BaseNode * pBuildRoot = pBuildGadget->build()->node();
	if (! pBuildRoot )
		return false;

	NounStructure * pBuild = NULL;

	int nPower = 0;
	int nHabitat = 0;
	int nFood = 0;
	int nWorkers = 0;
	int nResearch = 0;

	for(int i=0;i<pPlanet->childCount();++i)
	{
		NounStructure * pStructure = WidgetCast<NounStructure>( pPlanet->child(i) );
		if ( ! pStructure )
			continue;

		if ( pStructure->isBuilding() )
		{
			// found a incomplete structure... help build.
			useGadget( pBuildGadget, pStructure, false );
			return true;
		}

		nPower += pStructure->power();
		nHabitat += pStructure->habitat();
		nFood += pStructure->food();
		nWorkers += pStructure->workers();
		nResearch += pStructure->research();
	}

	if ( CountChildren<StructureHub>( pPlanet ) < 1 )
		pBuild = FindChild<StructureHub>( pBuildRoot );
	if ( pBuild == NULL && CountChildren<StructureBarracks>( pPlanet ) < 1 )
		pBuild = FindChild<StructureBarracks>( pBuildRoot );
	if ( pBuild == NULL && CountChildren<StructureMine>( pPlanet ) < 2 )
		pBuild = FindChild<StructureMine>( pBuildRoot );
	if ( pBuild == NULL && (CountChildren<StructurePower>( pPlanet ) < 1 || nPower <= 0) )
		pBuild = FindChild<StructurePower>( pBuildRoot );
	if ( pBuild == NULL && (CountChildren<StructureDome>( pPlanet ) < 4 || nWorkers > nHabitat) )
		pBuild = FindChild<StructureDome>( pBuildRoot );
	if ( pBuild == NULL && (CountChildren<StructureFarm>( pPlanet ) < 4 || nWorkers > nFood) )
		pBuild = FindChild<StructureFarm>( pBuildRoot );
	if ( pBuild == NULL && CountChildren<StructureLab>( pPlanet ) < 2 )
		pBuild = FindChild<StructureLab>( pBuildRoot );
	if ( pBuild == NULL && CountChildren<StructurePort>( pPlanet ) < 1 )
		pBuild = FindChild<StructurePort>( pBuildRoot );
	if ( pBuild == NULL && CountChildren<StructureFactory>( pPlanet ) < 2 )
		pBuild = FindChild<StructureFactory>( pBuildRoot );
	if ( pBuild == NULL && CountChildren<StructureMine>( pPlanet ) < 4 )
		pBuild = FindChild<StructureMine>( pBuildRoot );
	if ( pBuild == NULL && CountChildren<StructureDepot>( pPlanet ) < 1 )
		pBuild = FindChild<StructureDepot>( pBuildRoot );
	// build shipyard if the planet is Terran...
	if ( pBuild == NULL && pPlanet->baseHabitat() >= 50 && CountChildren<StructureShipyard>( pPlanet ) < 1 )
	{
		if ( nResearch < 65 )
			pBuild = FindChild<StructureLab>( pBuildRoot );
		else
			pBuild = FindChild<StructureShipyard>( pBuildRoot );
	}
	// build shield if available..
	if ( pBuild == NULL && CountChildren<StructureShield>( pPlanet ) < 1 )
		pBuild = FindChild<StructureShield>( pBuildRoot );
	// lastly, build defense bases until full..
	if ( pBuild == NULL )
		pBuild = FindChild<StructureDefense>( pBuildRoot );

	// TODO: Upgrade structures...

	if ( ! pBuild )
		return false;
	
	// for the sake of getting it done, just pick a random position on the planet and find a hex..
	Vector3 vOffset( RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ) );
	vOffset.normalize();
	vOffset *= pPlanet->radius();

	int nHex = pPlanet->findHex( vOffset, true );
	if ( nHex < 0 )
		return false;

	NounDropTarget::Ref pDropTarget = new NounDropTarget( pPlanet, nHex );

	// we may need to wait for resources to build up before we can build...
	if ( VerbBuildStructure::canBuild( this, pDropTarget, pBuild ) )
		Verb::Ref( new VerbBuildStructure( this, pDropTarget, pBuild ) );

	return true;
}

static Constant FALLBACK_DEFEND_DISTANCE("FALLBACK_DEFEND_DISTANCE", 1000.0f );

bool NounShip::updateFallbackOrder()
{
	Noun * pTarget = m_OrderTarget;
	if (! pTarget )
		return false;

	bool bCornered = false;

	float fTargetDistance = RealDistanceFromTarget( this, pTarget ); //(worldPosition() - pTarget->worldPosition()).magnitude();
	if( fTargetDistance < FALLBACK_DEFEND_DISTANCE )
	{
		NounShip * pEnemy = findEnemyShip( DEFAULT_SHIP_BRAVERY, FALLBACK_DEFEND_DISTANCE );
		if ( pEnemy != NULL && fTargetDistance < FALLBACK_DEFEND_DISTANCE )
		{
			bCornered = true;		// ok, we have fallen back, but we still have enemies... so FIGHT to the death!
			if ( m_Command != EVADE && m_Command != ENGAGE )
				verbCommand( ENGAGE, pEnemy );
		}
	}

	if (! bCornered )
	{
		if ( canOrbit( pTarget ) )
			verbCommand( ORBIT, pTarget );
		else
			verbCommand( FOLLOW, pTarget );
	}

	return needFuel() || needAmmo() || damageRatio() > 0.01f;
}

//---------------------------------------------------------------------------------------------------

bool NounShip::findBestOrder()
{
	switch( type() )
	{
	case SCOUT:
		if ( acquireReconTarget() )
			return true;
		return false;
	case FIGHTER:
	case FRIGATE:
	case DESTROYER:
	case CRUISER:
	case DREAD:
		if ( acquireCombatTarget() )
			return true;
		return false;
	case STATION:
		if ( acquireCombatTarget() )
			return true;
		if ( acquireReloadTarget() )
			return true;
		return false;
	case ENGINEER:
		if ( acquireBuildTarget() )
			return true;
		return false;
	case SUPPLY:
		if ( acquireReloadTarget() )
			return true;
		return false;
	case TRANSPORT:
		if ( acquireCaptureTarget() )
			return true;
		if ( acquireTradeTarget() )
			return true;
		return false;
	case PLATFORM:
	case PLATFORM_SUPPLY:
		verbOrder( DEFEND, this, this );
		return true;
	}

	return false;
}

bool NounShip::returnHome()
{
	// look for the closest planet/depot to refuel and reload ...
	m_pHome = findDepot();
	if (! validateTarget( m_pHome ) )
	{
		m_pHome = findPlanet( true );
		if (! validateTarget( m_pHome ) )
			m_pHome = findHomeJumpGate();
	}
	if ( ! validateTarget( m_pHome ) )
		return false;

	if ( canOrbit( m_pHome) )
		verbCommand( ORBIT, m_pHome );
	else
		verbCommand( FOLLOW, m_pHome ); 

	// we don't want to issue an order, it can't be interrupted if an order is found..
	//verbOrder( MOVE, m_pHome, this );

	return true;
}

bool NounShip::updateCommand()
{
	if (! isLocal() )
		return false;

	switch( m_Command )
	{
	case ORBIT:
		if ( validateTarget( m_CommandTarget ) )
		{
			if ( VerbEnterOrbit::canOrbit( this, m_CommandTarget ) )
			{
				if ( moveToTarget( m_CommandTarget, radius() + m_CommandTarget->radius(), 
					MAX_SHIP_ORBIT_DISTANCE, SHIP_DEFAULT_SPEED, true, false ) == MOVE_DONE )
				{
					if ( VerbEnterOrbit::canEnterOrbit( this, m_CommandTarget ) )
					{
						// enter orbit
						Verb::Ref( new VerbEnterOrbit( this, m_CommandTarget, SHIP_ORBIT_VELOCITY ) );
						// command complete
						verbCommand( NOCOMMAND, NULL );
					}
					else
					{
						// stop the ship
						controlVelocity( 0.0f );
					}
				}
			}
			else
				verbCommand( NOCOMMAND, NULL );
		}
		break;
	case FOLLOW:
	case SHADOW:
		if ( validateTarget( m_CommandTarget ) )
		{
			// if we are following a ship, then if they jump then jump to the same destination
			if ( WidgetCast<NounShip>( m_CommandTarget.pointer() ) )
			{
				NounShip * pFollow = (NounShip *)m_CommandTarget.pointer();

				if ( isFriend( pFollow ) )
				{
					GadgetJumpDrive * pMyDrive = jumpDrive();
					GadgetJumpDrive * pFollowDrive = pFollow->jumpDrive();
					if ( pMyDrive != NULL && pFollowDrive != NULL )
					{
						if ( (pFollowDrive->jumping() || pFollowDrive->engaged()) )
						{
							if ( !pMyDrive->jumping() && !pMyDrive->engaged() )
							{
								Noun * pJumpTarget = pFollowDrive->jumpTarget();
								if ( pJumpTarget != NULL )
								{
									// get the world position of the jump target
									Vector3 jumpTarget( pJumpTarget->worldPosition() );
									// get it's radius
									float jumpTargetRadius = pJumpTarget->radius();

									// if the ship we are following is currently aligning for jump
									bool bJumpNow = true;
									if ( pFollowDrive->jumping() )	
									{	
										// add the formation offset to the jumpTarget
										// TODO: only add the formation offset if we're already close enough to be in formation
										jumpTarget += (worldPosition() - pFollow->worldPosition());

										// check how long it would take our ship to align for the jump
										Vector3 jump( jumpTarget - worldPosition() );
										float jumpHeading = atan2( jump.x, jump.z );
										float delta = AngleDelta( NormalizeAngle( heading() ), jumpHeading );
										int yawTicks = fabs( delta ) / (maxYaw() * TICK_DURATION_S);
										
										// seems to be always zero for AI ships when aligning.
										//int leaderYawTicks = pFollowDrive->jumpTime();
										// so calculate manually how long it takes the leader to align
										Vector3 leaderJump( jumpTarget - pFollow->worldPosition() );
										float leaderJumpHeading = atan2( leaderJump.x, leaderJump.z );
										float leaderDelta = AngleDelta( NormalizeAngle( pFollow->heading() ), leaderJumpHeading );
										int leaderYawTicks = fabs( leaderDelta ) / (pFollow->maxYaw() * TICK_DURATION_S);
							
										// does it take the leader more time to align than us ?
										if( leaderYawTicks > yawTicks + TICKS_PER_SECOND / 2)
											bJumpNow = false;	// then don't jump yet
										
									}

									if( bJumpNow )
									{
										useGadget( pMyDrive, new NounJumpTarget( jumpTarget, jumpTargetRadius, (GameContext *)context() ), false );
										// send the verb now, otherwise we might try to jump twice, which will end up aborting our jump!
										useGadgets();
									}
								}
							}

							break;
						}
					}
				}
			}

			Noun * pTarget = m_CommandTarget;
			if ( WidgetCast<NounJumpGate>( pTarget ) )
			{
				// move inside the target
				if ( moveToTarget( pTarget, 0.0f, 0.0f, 1.0f, false, false ) == MOVE_DONE )
					controlVelocity( 0 );
			}
			else
			{
				// move within follow distance, then match velocity and heading
				float fWithin = m_Command == FOLLOW ? SHIP_FOLLOW_DISTANCE : SHIP_SHADOW_DISTANCE;
				if ( moveToTarget( pTarget, radius() + pTarget->radius(), fWithin, 1.0f, true, false ) == MOVE_DONE )
				{
					Vector3 vWorldVelocity( pTarget->worldVelocity() );
					float fVelocity = vWorldVelocity.magnitude();
					float fYaw = m_fSetHeading;

					if ( fVelocity > 0.0f )
					{
						vWorldVelocity *= 1.0f / fVelocity;		// normalize the vector..
						fYaw = atan2( vWorldVelocity.x, vWorldVelocity.z );
					}

					controlHeadingVelocity( fYaw, fVelocity );
				}
			}
		}
		else
			verbCommand( NOCOMMAND, NULL );
		break;
	case EVADE:
		if ( validateTarget( m_CommandTarget ) )
		{
			Vector3 delta( m_CommandTarget->worldPosition() - worldPosition() );
			
			// check distance to target
			float distance = delta.magnitude();
			//delta.normalize();

			distance -= (m_CommandTarget->radius() + radius());
			if ( distance < SHIP_EVADE_DISTANCE )
			{
				static dword EVADE_VECTOR_TICKS = TICKS_PER_SECOND * EVADE_VECTOR_INTERVAL;
				if ( (m_nLastEvadeVector + EVADE_VECTOR_TICKS) < tick() )
				{
					delta.normalize();
					float fEvadeAngle = atan2( delta.x, delta.z ) + RandomFloat( -PI4, PI4 );

					m_nLastEvadeVector = tick();
					m_vEvadeVector.x = sinf( fEvadeAngle );
					m_vEvadeVector.y = 0.0f;
					m_vEvadeVector.z = cosf( fEvadeAngle );
				}

				NounZoneTarget target( worldPosition() - (m_vEvadeVector * (SHIP_EVADE_DISTANCE * 2)), context() );
				moveToTarget( &target, 0, 0, 1.0f, 0, false, false );		
			}
			else
				verbCommand( NOCOMMAND, NULL );
		}
		else
			verbCommand( NOCOMMAND, NULL );
		break;
	case ENGAGE:
		if ( validateTarget( m_CommandTarget ) )
		{
			bool bForceJump = false;

			// calculate an intercept position for our target based on our velocity and theirs..
			Vector3 vIntercept = m_CommandTarget->worldPosition();
			if ( WidgetCast<NounShip>( m_CommandTarget ) )
			{
				NounShip * pTarget = (NounShip *)m_CommandTarget.pointer();
				if ( pTarget->maxVelocity() > maxVelocity() )		// ship must be faster then ours 
				{
					// if the distance is outside of weapons range, then consider jumping ahead of our target...
					Vector3 vDelta( vIntercept - worldPosition() );
					float fInterceptDistance = vDelta.magnitude();
					if ( fInterceptDistance > MIN_INTERCEPT_DISTANCE )
					{
						int nChance = JUMP_INTERCEPT_CHANCE + (m_fCunning * JUMP_INTERCEPT_CHANCE);
						if ( (rand() % 100) < nChance )
						{
							vIntercept += pTarget->worldVelocity() * JUMP_INTERCEPT_TIME;
							bForceJump = true;
						}
					}
				}
			}

			NounZoneTarget::Ref pTarget = new NounZoneTarget( vIntercept, context() );
			if ( moveToTarget( pTarget, radius() + m_CommandTarget->radius(), 
				SHIP_ENGAGE_DISTANCE, SHIP_ENGAGE_SPEED, false, bForceJump ) == MOVE_DONE )
			{
				if ( maxVelocity() > 5.0f )
				{
					// begin evading, after evading the command will be set back to NOCOMMAND
					m_Command = EVADE;
				}
			}
		}
		else
			verbCommand( NOCOMMAND, NULL );
		break;
	case STOP:
		controlVelocity( 0.0f );		// stop ship
		break;
	case REFUEL:
		if ( validateTarget( m_CommandTarget ) )
		{
			if ( orbiting() != m_CommandTarget )
			{
				if ( VerbEnterOrbit::canOrbit( this, m_CommandTarget ) )
				{
					if ( moveToTarget( m_CommandTarget, radius() + m_CommandTarget->radius(), 
						MAX_SHIP_ORBIT_DISTANCE, 1.0f , true, false ) == MOVE_DONE )
					{
						if ( VerbEnterOrbit::canEnterOrbit( this, m_CommandTarget ) )
						{
							// enter orbit
							Verb::Ref( new VerbEnterOrbit( this, m_CommandTarget, SHIP_ORBIT_VELOCITY ) );
						}
						else
						{
							// stop the ship
							controlVelocity( 0.0f );
						}
					}
				}
				else
					verbCommand( NOCOMMAND, NULL );
			}
			else
			{
				// we are in orbit around our command target, wait for drive to be fully fueled!
				GadgetJumpDrive * pDrive = jumpDrive();
				if ( !validateNoun( pDrive ) || pDrive->fuel() >= pDrive->maxFuel() )
					verbCommand( NOCOMMAND, NULL );		// no drive or drive fueled, command completed!
			}
		}
		else
			verbCommand( NOCOMMAND, NULL );
		break;
	}

	return m_Command != NOCOMMAND;
}

void NounShip::updatePointDefense()
{
	// handle point defense
	if ( isLocal() )
	{
		for(int j=0;j<contactCount();++j)
		{
			NounGame * pContact = WidgetCast<NounGame>( contact( j ) );
			if ( pContact != NULL && pContact->enablePD() )
			{
				if ( isFriend( pContact ) )
					continue;

				// any other weapons on this ship have this projectile as their target
				GadgetBeamWeapon * pBeam = NULL;
				bool bTargeted = false;

				for(int i=0;i<m_Gadgets.size() && !bTargeted;i++)
				{
					GadgetBeamWeapon * pCheck = WidgetCast<GadgetBeamWeapon>( m_Gadgets[i] );
					if ( pCheck != NULL && pCheck->pointDefense() )
					{
						if ( pCheck->target() == pContact )
							bTargeted = true;
						else if ( pCheck->usable( pContact, false ) )
							pBeam = pCheck;
					}
				}

				// if projectile not targeted, check for usablitly
				if (! bTargeted && pBeam != NULL )
				{
					// fire weapon at target, set the target first so no other beam weapons attempt to fire
					// at the same projectile this loop
					pBeam->setTarget( pContact );
					// issue the verb to use this beam weapon on the target
					useGadget( pBeam, pContact, false );
					useGadgets();
				}
			}
		}
	}
}

//----------------------------------------------------------------------------

bool NounShip::avoidCollision()
{
	// calculate how long it will take to stop
	float fAcceleration = maxAcceleration();
	float t2 = m_fVelocity / (fAcceleration * 2.0f);
	float r2 = radius() * 2.0f;
	
	Vector3 origin( worldPosition() );
	Vector3 direction( worldVelocity() * t2 );

	for(int i=0;i<contactCount();i++)
	{
		Noun * pContact = contact( i );
		if ( WidgetCast<NounPlanet>( pContact ) == NULL && 
			WidgetCast<NounStar>( pContact ) == NULL )
			continue;

		Vector3 intersect;
		if ( pContact->intersect( r2, origin, direction, intersect ) )
		{
			TRACE( CharString().format("NounShip::updateCommand, collision alert with %s", pContact->name()) );

			Vector3 normalDelta( direction );
			normalDelta.normalize();

			Vector3 collidePosition( pContact->worldPosition() );
			Vector3 collideDelta( collidePosition - origin );
			float collideRange = collideDelta.magnitude();
			float collideRadius = (pContact->radius() + r2) * 2.0f;		// add 50% to radius to make sure we clear the object
			collideDelta.normalize();

			// use the 2 normalized delta vectors, set their lengths to the distance from our position
			// to the collision target, then subtract those 2 vectors, normalize, and scale by the radius of the object
			Vector3 X( normalDelta * collideRange );
			Vector3 Y( collideDelta * collideRange );
			Vector3 Z( X - Y );
			Z.y = 0.0f;		// keep it 2D

			Z.normalize();

			//TRACE( CharString().format("X = %.1f,%.1f,%.1f, Y = %.1f,%.1f,%.1f, Z = %.1f,%.1f,%.1f",
			//	X.x,X.y,X.z, Y.x,Y.y,Y.z, Z.x,Z.y,Z.x) );

			// set the new target position
			Vector3 target( collidePosition + (Z * collideRadius) );
			// recalculate the delta vector
			Vector3 delta( target - origin );

			// turn towards escape route, use last velocity passed to verbControl()
			controlHeadingVelocity( atan2( delta.x, delta.z ), 0.0f );
			return true;
		}
	}

	return false;
}

//----------------------------------------------------------------------------

float NounShip::scoreTarget( Noun * pTarget )
{
	if ( WidgetCast<NounDropTarget>( pTarget ) )
		pTarget = ((NounDropTarget *)pTarget)->planet();
	pTarget = pTarget->zoneParent();

	float fValue = value();
	float fTargetValue = 1.0f;

	if ( WidgetCast<NounShip>( pTarget ) )
		fTargetValue = ((NounShip *)pTarget)->value();
	else if ( WidgetCast<NounPlanet>( pTarget ) )
		fTargetValue = ((NounPlanet *)pTarget)->value();

	return fValue / Max( (fValue + fTargetValue), 1.0f );
}

bool NounShip::canAttack( Noun * pTarget )
{
	if ( WidgetCast<NounShip>( pTarget ) )
		return true;

	if ( isTargetPlanet( pTarget ) )
	{
		for(int i=0;i<m_Gadgets.size();i++)
			if ( m_Gadgets[i]->type() == NounGadget::WEAPON_BOMB )
				return true;
	}

	return false;
}

bool NounShip::isMaxFuel()
{
	GadgetJumpDrive * pDrive = jumpDrive();
	if ( pDrive != NULL )
		return pDrive->fuel() >= pDrive->maxFuel();

	return true;
}

bool NounShip::needFuel()
{
	GadgetJumpDrive * pDrive = jumpDrive();
	if (! pDrive )
		return false;

	float f = ((float)pDrive->fuel()) / ((float)pDrive->maxFuel());
	return f <= NEED_FUEL_PERCENT;
}

bool NounShip::needAmmo()
{
	// check all weapons, if any weapon ammo level is below a certain level then this will return true...
	for(int i=0;i<childCount();++i)
	{
		GadgetWeapon * pWeapon = WidgetCast< GadgetWeapon >( child(i) );
		if (! pWeapon )
			continue;
		if ( pWeapon->ammoMax() <= 0 )
			continue;		// no ammo needed..
		float fAmmoLevel = ((float)pWeapon->ammo()) / ((float)pWeapon->ammoMax());
		if ( fAmmoLevel <= NEED_AMMO_PERCENT )
			return true;
	}

	return false;
}

bool NounShip::sellCargo()
{
	NounPlanet * pPlanet = WidgetCast<NounPlanet>( orbiting() );
	if ( validateTarget( pPlanet ) )
	{
		StructurePort * pPort = StructurePort::findPort( pPlanet );
		if (! pPort )
			return false;

		for(int i=0;i<childCount();i++)
		{
			NounCargo * pCargo = WidgetCast<NounCargo>( child(i) );
			if ( pCargo != NULL )
				Verb::Ref( new VerbUnload( this, pPort, pCargo ) );
		}

		return true;
	}

	return false;
}

bool NounShip::buyCargo( NounPlanet * pDestination )
{
	if ( pDestination == NULL )
		return false;		// invalid destination
	if ( podCount() > 0 )
		return true;		// keep trying to buy cargo until all pods arrive...
	int nBuyCount = availableCargo();
	if ( nBuyCount <= 0 )
		return false;		// no cargo space available

	NounPlanet * pPlanet = WidgetCast<NounPlanet>( orbiting() );
	if (! validateTarget( pPlanet ) )
		return false;		// not in orbit
	if ( pPlanet == pDestination )
		return false;		// no destination, don't bother
	if ( pPlanet->resourceDemand() > pDestination->resourceDemand() )
		return false;		// this planet has a higher resource demand

	StructurePort * pPort = StructurePort::findPort( pPlanet );
	if (! validateTarget( pPort ) )
		return false;		// no port found

	// everything ok, buy n cargo items and fill our cargo hold with resources..
	for(int i=0;i<nBuyCount;i++)
		Verb::Ref( new VerbLoad( this, pPort ) );

	return true;
}

NounShip * NounShip::findEnemyShip( float bravery, float within ) const
{
	ASSERT( bravery >= 0.0f && bravery <= 1.0f );

	float			myValue = value();
	Vector3			myPosition( worldPosition() );

	NounShip *		bestShip = NULL;
	float			bestShipValue = 32768.0f;

	for(int i=0;i<contactCount();i++)
	{
		NounShip * pShip = WidgetCast<NounShip>( contact( i ) );
		if ( pShip && pShip != this && !isFriend( pShip ) )
		{
			float distance = (myPosition - pShip->worldPosition()).magnitude();
			if ( distance > within )
				continue;

			// > 1.0 then the ship is more powerful than me, less < 1.0 the ship is weaker
			float value = float( pShip->value() ) / myValue;	
			// factor in the bravery, a lower bravery make me look for weaker ships than myself
			value *= (1.0f - bravery);
			// factor in the distance to the target
			value += distance * FIND_ENEMY_DISTANCE_VALUE;

			if ( value < bestShipValue )
			{
				bestShip = pShip;
				bestShipValue= value;
			}
		}
	}

	return( bestShip );
}

//---------------------------------------------------------------------------------------------------

bool NounShip::acquireReloadTarget()
{
	if ( needFuel() )
		return false;

	int nMyFactionId = factionId();
	// first off, check for any distress signals...
	GameContext::DistressList & list = gameContext()->getDistressList( nMyFactionId );
	for( GameContext::DistressList::iterator iDistress = list.begin(); iDistress != list.end(); ++iDistress )
	{
		GameContext::Distress * pDistress = *iDistress;
		if ( pDistress->m_nType != VerbDistress::SUPPLY )
			continue;			// ignore non-supply distress

		if ( WidgetCast<NounShip>( pDistress->m_pWho ) )
		{
			NounShip * pShip = (NounShip *)pDistress->m_pWho.pointer();
			if ( pShip == this )
				continue;		// don't respond to our own distress signal..
			GameContext::Order * pOrder = gameContext()->findOrder( factionId(), RELOAD, pShip );
			if ( pOrder )
				continue;		// another ship is already responding to this distress..

			verbOrder( RELOAD, pDistress->m_pWho, this );
			return true;
		}
	}

	NounShip * pBestReload = NULL;
	float fBestDistance = 0.0f;

	for(int j=0;j<context()->zoneCount();j++)
	{
		NodeZone * pZone = context()->zone( j );
		for(int i=0;i<pZone->childCount();i++)
		{
			NounShip * pShip = WidgetCast<NounShip>( pZone->child(i) );
			if (! pShip )
				continue;
			if (! isFriend( pShip ) )
				continue;
			if ( !pShip->needRepair( (Noun *)this ) 
				&& !pShip->needReload(  (Noun *)this ) )
				continue;
			GameContext::Order * pOrder = gameContext()->findOrder( factionId(), RELOAD, pShip );
			if ( pOrder )
				continue;		// another ship is already responding to this distress..

			float fDistance = (worldPosition() - pShip->worldPosition()).magnitude();
			if ( fDistance < fBestDistance || !pBestReload )
			{
				pBestReload = pShip;
				fBestDistance = fDistance;
			}
		}
	}

	if (! pBestReload )
		return false;

	verbOrder( RELOAD, pBestReload, this );
	return true;
}

bool NounShip::acquireReconTarget() 
{
	if ( needFuel() )
		return false;

	std::vector< NounPlanet * > targets;

	Vector3 vMyPosition( worldPosition() );
	for(int j=0;j<context()->zoneCount();j++)
	{
		NodeZone * pZone = context()->zone( j );
		for(int i=0;i<pZone->childCount();i++)
		{
			NounPlanet * pPlanet = WidgetCast<NounPlanet>( pZone->child(i) );
			if (! pPlanet )
				continue;

			float fDistance = (vMyPosition - pPlanet->worldPosition()).magnitude();
			if ( fDistance < MIN_RECON_DISTANCE )
				continue;		// planet is too close, next ...
			
			GameContext::Order * pOrder = gameContext()->findOrder( factionId(), RECON, pPlanet );
			if ( pOrder )
				continue;		// another ship is already scouting this planet.. 

			targets.push_back( pPlanet );
		}
	}

	if ( targets.size() <= 0 )
		return false;

	int nPick = rand() % targets.size();
	verbOrder( RECON, targets[ nPick ], this );

	return true;
}

bool NounShip::acquireCombatTarget()
{
	if ( needFuel() || needAmmo() )
		return false;

	int nMyFactionId = factionId();
	// first off, check for any distress signals...
	GameContext::DistressList & list = gameContext()->getDistressList( nMyFactionId );
	for( GameContext::DistressList::iterator iDistress = list.begin(); iDistress != list.end(); ++iDistress )
	{
		GameContext::Distress * pDistress = *iDistress;
		if ( pDistress->m_nType == VerbDistress::SUPPLY )
			continue;			// ignore supply distress

		if ( WidgetCast<NounShip>( pDistress->m_pWho ) )
		{
			NounShip * pShip = (NounShip *)pDistress->m_pWho.pointer();
			if ( pShip == this )
				continue;		// don't respond to our own distress signal..

			float fValue = pShip->value();
			GameContext::Order * pOrder = gameContext()->findOrder( factionId(), DEFEND, pShip );
			if ( pOrder != NULL )
				fValue -= pOrder->m_nScore;
			if ( fValue < 0.0f )
				continue;

			verbOrder( DEFEND, pShip, this );
			return true;
		}
		else if ( WidgetCast<NounPlanet>( pDistress->m_pWho ) )
		{
			NounPlanet * pPlanet = (NounPlanet *)pDistress->m_pWho.pointer();

			float fValue = pPlanet->value();

			GameContext::Order * pOrder = gameContext()->findOrder( factionId(), DEFEND, pPlanet );
			if ( pOrder != NULL )
				fValue -= pOrder->m_nScore;
			if ( fValue < 0.0f )
				continue;

			verbOrder( DEFEND, pPlanet, this );
			return true;
		}
	}

	Noun *			pBestTarget = NULL;
	float			fBestValue = 0.0f;
	float			fMyValue = value();
	Vector3			vPosition( worldPosition() );

	// now scan the world for a attack target...
	for(int j=0;j<context()->zoneCount();j++)
	{
		NodeZone * pZone = context()->zone( j );
		for(int i=0;i<pZone->childCount();i++)
		{
			NounGame * pNoun = WidgetCast<NounGame>( pZone->child(i) );
			if (! pNoun )
				continue;
			if (! pNoun->isDetected( nMyFactionId ) )
				continue;
			if ( isFriend( pNoun ) )
				continue;
			if (! canAttack( pNoun ) )
				continue;

			if ( WidgetCast<NounShip>( pNoun ) )
			{
				NounShip * pShip = (NounShip *)pNoun;

				// adjust value of ship based on distance ...
				float fDistance = (vPosition - pShip->worldPosition()).magnitude();
				float fValue = pShip->value() - (fDistance * DISTANCE_VALUE_SCALE);

				GameContext::Order * pOrder = gameContext()->findOrder( factionId(), ATTACK, pShip );
				if ( pOrder != NULL )
					fValue -= pOrder->m_nScore;

				if ( !pBestTarget || fValue > fBestValue )
				{
					pBestTarget = pNoun;
					fBestValue = fValue;
				}
			}
			else if ( WidgetCast<NounPlanet>( pNoun ) )
			{
				NounPlanet * pPlanet = (NounPlanet *)pNoun;

				if ( pPlanet->maxUnits() == 0 )
					continue;		// don't attack gas giants
				if ( pPlanet->structureCount() == 0 || pPlanet->unitCount() == 0 )
					continue;		// don't attack planets with no structures or units

				float fDistance = (vPosition - pPlanet->worldPosition()).magnitude();
				float fValue = pPlanet->value() - (fDistance * DISTANCE_VALUE_SCALE);

				GameContext::Order * pOrder = gameContext()->findOrder( factionId(), ATTACK, pPlanet );
				if ( pOrder != NULL )
					fValue -= pOrder->m_nScore;

				if ( (pPlanet->flags() & NounPlanet::FLAG_BLOCKADE) != 0 )
					fValue *= BLOCKADE_VALUE_SCALE;			// blockaded planets get bonus value
				if ( (pPlanet->flags() & NounPlanet::FLAG_HAS_SHIPYARD) != 0 )
					fValue *= SHIPYARD_VALUE_SCALE;			// blockaded planets get bonus value

				if ( !pBestTarget || fValue > fBestValue )
				{
					pBestTarget = pNoun;
					fBestValue = fValue;
				}
			}

		}
	}

	if (! pBestTarget )
		return false;

	verbOrder( ATTACK, pBestTarget, this );
	return true;
}

bool NounShip::acquireBuildTarget()
{
	if ( needFuel() )
		return false;

	NounPlanet *	pBestTarget = NULL;
	float			fBestValue = 0.0f;

	Vector3 vMyPosition( worldPosition() );
	for(int j=0;j<context()->zoneCount();j++)
	{
		NodeZone * pZone = context()->zone( j );
		for(int i=0;i<pZone->childCount();i++)
		{
			NounPlanet * pPlanet = WidgetCast<NounPlanet>( pZone->child(i) );
			if (! pPlanet )
				continue;
			if (! isFriend( pPlanet ) )
				continue;		// planet must be friendly..
			if ( pPlanet->structureCount() >= pPlanet->maxStructures() )
				continue;		// planet is fully built..
			GameContext::Order * pOrder = gameContext()->findOrder( factionId(), BUILD, pPlanet );
			if ( pOrder != NULL )
				continue;		// another ship has an order to build this planet..

			float fDistance = (vMyPosition - pPlanet->worldPosition()).magnitude();
			float fValue = pPlanet->value() - (fDistance * DISTANCE_VALUE_SCALE);

			if ( !pBestTarget || fValue > fBestValue )
			{
				pBestTarget = pPlanet;
				fBestValue = fValue;
			}
		}
	}

	if (! pBestTarget )
		return false;

	verbOrder( BUILD, pBestTarget, this );
	return true;
}

bool NounShip::acquireCaptureTarget() 
{
	if ( needFuel() )
		return false;

	NounPlanet *	pBestTarget = NULL;
	float			fBestValue = 0.0f;

	Vector3 vMyPosition( worldPosition() );
	for(int j=0;j<context()->zoneCount();j++)
	{
		NodeZone * pZone = context()->zone( j );
		for(int i=0;i<pZone->childCount();i++)
		{
			NounPlanet * pPlanet = WidgetCast<NounPlanet>( pZone->child(i) );
			if (! pPlanet )
				continue;
			if ( isFriend( pPlanet ) )
				continue;		// planet must not be friendly..
			if ( pPlanet->maxUnits() <= 0 )
				continue;		// planet must be able to support life... no gas giants

			float fDistance = (vMyPosition - pPlanet->worldPosition()).magnitude();
			float fValue = pPlanet->value() - (fDistance * DISTANCE_VALUE_SCALE);
			
			// scale value by the number of troops on the planet.. fewer troops means higher value..
			fValue *= 1.0f - (((float)pPlanet->friendlyUnitCount()) / ((float)pPlanet->maxUnits()));
			// make sure we don't already have other ships trying to capture this planet..
			GameContext::Order * pOrder = gameContext()->findOrder( factionId(), CAPTURE, pPlanet );
			if ( pOrder != NULL )
				fValue -= pOrder->m_nScore;

			if ( !pBestTarget || fValue > fBestValue )
			{
				pBestTarget = pPlanet;
				fBestValue = fValue;
			}
		}
	}

	if (! pBestTarget )
		return false;

	// send this ship the order..
	verbOrder( CAPTURE, pBestTarget, this );
	return true;
}

bool NounShip::acquireTradeTarget()
{
	if ( needFuel() )
		return false;

	NounPlanet *	pBestTarget = NULL;
	float			fBestValue = 0.0f;

	Vector3 vMyPosition( worldPosition() );
	for(int j=0;j<context()->zoneCount();j++)
	{
		NodeZone * pZone = context()->zone( j );
		for(int i=0;i<pZone->childCount();i++)
		{
			NounPlanet * pPlanet = WidgetCast<NounPlanet>( pZone->child(i) );
			if (! pPlanet )
				continue;
			if (! isFriend( pPlanet ) )
				continue;		// planet must be friendly..
			if ( (pPlanet->flags() & NounPlanet::FLAG_HAS_PORT) == 0 )
				continue;		// planet must have a port..
			if ( (pPlanet->flags() & NounPlanet::FLAG_BLOCKADE) != 0 )
				continue;		// planet is currently blockaded..

			float fDistance = (vMyPosition - pPlanet->worldPosition()).magnitude();
			float fValue = pPlanet->value() - (fDistance * DISTANCE_VALUE_SCALE);

			GameContext::Order * pOrder = gameContext()->findOrder( factionId(), TRADE, pPlanet );
			if ( pOrder != NULL )
				fValue -= pOrder->m_nScore;

			if ( (pPlanet->flags() & NounPlanet::FLAG_HAS_SHIPYARD) != 0 )
				fValue *= SHIPYARD_VALUE_SCALE;			// blockaded planets get bonus value

			if ( !pBestTarget || fValue > fBestValue )
			{
				pBestTarget = pPlanet;
				fBestValue = fValue;
			}
		}
	}

	if (! pBestTarget )
		return false;

	verbOrder( TRADE, pBestTarget, this );
	return true;
}

//---------------------------------------------------------------------------------------------------

Noun * NounShip::findDepot() const
{
	Noun *			pBestDepot = NULL;
	float			fBestDistance = 0.0f;

	Vector3	myPosition( worldPosition() );
	for(int j=0;j<context()->zoneCount();j++)
	{
		NodeZone * pZone = context()->zone( j );
		for(int i=0;i<pZone->childCount();i++)
		{
			Noun * pNoun = WidgetCast<Noun>( pZone->child(i) );
			if (! pNoun )
				continue;
			if ( WidgetCast<NounPlanet>(pNoun ) )
			{
				NounPlanet * pPlanet = (NounPlanet *)pNoun;
				if ( !isFriend( pPlanet ) )
					continue;

				float fDistance = (pPlanet->worldPosition() - myPosition).magnitude2();
				if ( (pPlanet->flags() & NounPlanet::FLAG_HAS_DEPOT) == 0 )
					fDistance *= 1.5f;		// consider planets without depots further

				if ( !pBestDepot || fDistance < fBestDistance )
				{
					pBestDepot = pPlanet;
					fBestDistance = fDistance;
				}
			}
			//else if ( WidgetCast<NounShip>( pNoun ) )
			//{
			//	NounShip * pShip = (NounShip *)pNoun;
			//	if ( !isFriend( pShip ) )
			//		continue;
			//	if ( pShip == this )
			//		continue;
			//	if ( type() == NounShip::SUPPLY ||
			//		type() == NounShip::STATION ||
			//		type() == NounShip::PLATFORM_SUPPLY )
			//		continue;		// supply ships must orbit a friendly planet..
			//	if ( pShip->type() != NounShip::SUPPLY &&
			//		pShip->type() != NounShip::STATION &&
			//		pShip->type() != NounShip::PLATFORM_SUPPLY )
			//		continue;

			//	float fDistance = (pShip->worldPosition() - myPosition).magnitude2();
			//	if ( !pBestDepot || fDistance < fBestDistance )
			//	{
			//		pBestDepot = pShip;
			//		fBestDistance = fDistance;
			//	}
			//}
		}
	}

	return pBestDepot;
}


NounPlanet * NounShip::findPlanet( bool bFriendly ) const
{
	NounPlanet *	pBestPlanet = NULL;
	float			fBestDistance = 0.0f;

	Vector3	myPosition( worldPosition() );
	for(int j=0;j<context()->zoneCount();j++)
	{
		NodeZone * pZone = context()->zone( j );
		for(int i=0;i<pZone->childCount();i++)
		{
			NounPlanet * pPlanet = WidgetCast<NounPlanet>( pZone->child(i) );
			if (! pPlanet )
				continue;
			if ( bFriendly && !isFriend( pPlanet ) )
				continue;

			float fDistance = (pPlanet->worldPosition() - myPosition).magnitude();
			if ( bFriendly && (pPlanet->flags() & NounPlanet::FLAG_HAS_DEPOT) != 0 )
				fDistance *= 0.5f;		// consider planets with depots closer..

			if ( !pBestPlanet || fDistance < fBestDistance )
			{
				pBestPlanet = pPlanet;
				fBestDistance = fDistance;
			}
		}
	}

	return pBestPlanet;
}

NounPlanet * NounShip::findUnits() const
{
	NounPlanet *	pBestPlanet = NULL;
	float			fBestValue = 0.0f;

	Vector3	myPosition( worldPosition() );
	for(int j=0;j<context()->zoneCount();j++)
	{
		NodeZone * pZone = context()->zone( j );
		for(int i=0;i<pZone->childCount();i++)
		{
			NounPlanet * pPlanet = WidgetCast<NounPlanet>( pZone->child(i) );
			if (! pPlanet )
				continue;
			if ( !isFriend( pPlanet ) )
				continue;
			int nUnitCount = pPlanet->friendlyUnitCount();
			if ( nUnitCount <= (int)MIN_PLANET_UNITS )
				continue;

			float fDistance = (pPlanet->worldPosition() - myPosition).magnitude();
			float fValue = (nUnitCount * UNIT_VALUE) - (fDistance * DISTANCE_VALUE_SCALE);

			if ( !pBestPlanet || fValue > fBestValue )
			{
				pBestPlanet = pPlanet;
				fBestValue = fValue;
			}
		}
	}

	return pBestPlanet;

}

NounJumpGate * NounShip::findHomeJumpGate() const
{
	Vector3	myPosition( worldPosition() );

	NounJumpGate *	pBestJumpGate = NULL;
	float			fBestRange = SHIP_FIND_RANGE;

	for(int j=0;j<context()->zoneCount();j++)
	{
		NodeZone * pZone = context()->zone( j );
		for(int i=0;i<pZone->childCount();i++)
		{
			NounJumpGate * pJumpGate = WidgetCast<NounJumpGate>( pZone->child(i) );
			if ( validateTarget( pJumpGate ) && pJumpGate->findDestination() == NULL )
			{
				float fRange = (myPosition - pJumpGate->worldPosition()).magnitude();
				if ( !pBestJumpGate || fRange < fBestRange )
				{
					pBestJumpGate = pJumpGate;
					fBestRange= fRange;
				}
			}
		}
	}

	return pBestJumpGate;
}

NounJumpGate * NounShip::findJumpGate( Noun * pDestination )
{
	if ( pDestination == NULL )
		return NULL;		// invalid destination

	Vector3	myPosition( worldPosition() );
	Vector3 dstPosition( pDestination->worldPosition() );
	float fDistance = (myPosition - dstPosition).magnitude();

	NounJumpGate *	pBestJumpGate = NULL;
	float			fBestDistance = fDistance;
	float			fBestTotalDistance = fDistance;

	for(int j=0;j<context()->zoneCount();j++)
	{
		NodeZone * pZone = context()->zone( j );
		for(int i=0;i<pZone->childCount();i++)
		{
			NounJumpGate * pJumpGate = WidgetCast<NounJumpGate>( pZone->child(i) );
			if ( validateTarget( pJumpGate ) )
			{
				Noun * pGateDestination = pJumpGate->findDestination();
				if ( pGateDestination != NULL )
				{
					float fDistance = (myPosition - pJumpGate->worldPosition()).magnitude();
					if ( fDistance > fBestDistance )
						continue;
					float fTotalDistance = fDistance + (dstPosition - pGateDestination->worldPosition()).magnitude();
					if ( fTotalDistance > fBestTotalDistance )
						continue;

					pBestJumpGate = pJumpGate;
					fBestDistance = fDistance;
					fBestTotalDistance = fTotalDistance;
				}
			}
		}
	}

	return pBestJumpGate;
}

NounPlanet * NounShip::findTradeTarget( NounPlanet * pHome )
{
	if (! pHome )
		return NULL;
	if ( !isFriend( pHome ) )
		return NULL;		// home planet has been captured
	if ( orbiting() != pHome )
		return pHome;		// if we are not orbiting our home, then always return back home to sell

	// find the best export trade route
	NounPlanet * pBestTarget = NULL;
	float fBestScore = 0.0f;

	StructurePort * pPort = StructurePort::findPort( pHome );
	if ( pPort == NULL )
		return NULL;		// home port scrapped or destroyed

	// parse our home planet export list, find the trade route with the highest demand score
	for(int i=0;i<pHome->tradeRoutes();i++)
	{
		NounPlanet * pDestination = pHome->tradeRoute( i );
		if (! validateTarget( pDestination ) )
			continue;		// planet not found, skip

		float fScore = pDestination->resourceDemand() - pHome->resourceDemand();
		if ( pBestTarget == NULL || fScore > fBestScore )
		{
			pBestTarget = pDestination;
			fBestScore = fScore;
		}
	}

	if ( fBestScore < 0.0f || pBestTarget == NULL )
		return pHome;		// demand is not higher, just stay home

	return pBestTarget;
}

//----------------------------------------------------------------------------

void NounShip::onAttacked( Noun * pFrom, int nDamage, dword damageType )
{
	if ( userId() == 0 && isLocal() )
	{
		m_bAttacked = true;
		m_pLastAttacker = pFrom;

		if ( isEnemy( pFrom ) && WidgetCast<NounShip>( pFrom ) != NULL )
		{
			// always try to return fire ... 
			if( command() != ENGAGE && command() != EVADE )	
				verbCommand( ENGAGE, pFrom );	
		}

		// if damage is very bad, consider a emergency jump...
		if ( damageRatioInv() < EMERGENCY_JUMP_DAMAGE && jumpDrive() != NULL )
		{
			if ( (rand() % 100) < EMERGENCY_JUMP_CHANCE )
				useGadget( jumpDrive(), NULL, true );
		}

		static dword ATTACK_DISTRESS_TICKS = (dword)(TICKS_PER_SECOND * SHIP_AI_ATTACK_DISTRESS);
		if ( (m_nLastDistress + ATTACK_DISTRESS_TICKS) < tick() && isEnemy( pFrom ) )
		{
			m_nLastDistress = tick();
			Verb::Ref( new VerbDistress( this, VerbDistress::ATTACKED ) );
		}

	}
}

//----------------------------------------------------------------------------
//EOF
